/*
 * This file is part of Plop Messenger.
 *
 * Plop Messenger is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  Plop Messenger is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Plop Messenger.  If not, see <http://www.gnu.org/licenses/>.
 */
package pm.net;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import pm.BadPasswordException;
import pm.Constantes;
import pm.Message;
import pm.MessageMaker;

public class PosteMsg implements PosteUDPListener {

	private Map<String, Set<InetAddress>> clients = new HashMap<String, Set<InetAddress>>();
	private Map<InetAddress, Long> lastActivity = new HashMap<InetAddress, Long>();
	private List<PosteMsgListener> listeners = new ArrayList<PosteMsgListener>();
	private Map<String, String> pass = new HashMap<String, String>();

	private PosteUDP posteUDP;

	public PosteMsg(int port) throws SocketException {
		this(new PosteUDP(port));
	}

	public PosteMsg(PosteUDP posteUDP) {
		this.posteUDP = posteUDP;
		posteUDP.addListener(this);
		posteUDP.start();
	}

	public void addListener(PosteMsgListener e) {
		listeners.add(e);
	}

	private void broadcastMessage(Message m) {
		byte[] data = MessageMaker.cryptMessageWith(m, pass.get(m.getTo())
				.getBytes());
		DatagramPacket packet = new DatagramPacket(data, data.length);
		try {
			packet.setAddress(InetAddress.getByName("255.255.255.255"));
			packet.setPort(Constantes.UDP_PORT);
			posteUDP.sendPacket(packet);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void connect(String chan, String from, String password) {
		pass.put(chan, password);
		clients.put(chan, new HashSet<InetAddress>());
		Message m = new Message(chan, from, Message.CLIENT_CONNECTED);
		broadcastMessage(m);
	}

	public void disconnect(String chan, String from) {
		Message m = new Message(chan, from, Message.CLIENT_EXITED);
		broadcastMessage(m);
		pass.remove(chan);
		clients.remove(chan);
	}

	@Override
	public void packetRecu(PosteUDP posteUDP, DatagramPacket packet) {
		String chan = MessageMaker.getChan(packet.getData(),
				packet.getOffset(), packet.getLength());
		if (pass.containsKey(chan)) {
			try {
				Message message = MessageMaker.decryptMessageWith(packet
						.getData(), packet.getOffset(), packet.getLength(),
						pass.get(chan).getBytes());
				if (!clients.get(message.getTo()).contains(packet.getAddress()))
					clients.get(message.getTo()).add(packet.getAddress());
				lastActivity.remove(packet.getAddress());
				lastActivity.put(packet.getAddress(), new Date().getTime());
				if (message.getType() != Message.PONG) {
					for (PosteMsgListener l : listeners) {
						l.messageReceived(this, message);
					}
				}
			} catch (BadPasswordException e) {
				Message message = new Message(chan, "", new String(packet
						.getData(), packet.getOffset(), packet.getLength()),
						Message.CRYPTED);
				for (PosteMsgListener l : listeners) {
					l.messageReceived(this, message);
				}
			}
		}
	}

	public void removeListener(PosteMsgListener e) {
		listeners.remove(e);
	}

	public void sendMessage(Message m) {
		byte[] data = MessageMaker.cryptMessageWith(m, pass.get(m.getTo())
				.getBytes());
		DatagramPacket packet = new DatagramPacket(data, data.length);
		String chan = m.getTo();
		for (InetAddress i : clients.get(chan)) {
			try {
				packet.setAddress(i);
				packet.setPort(Constantes.UDP_PORT);
				posteUDP.sendPacket(packet);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}
